package de.marmaro.krt.ffupdater.download

import android.app.DownloadManager
import android.app.DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR
import android.app.DownloadManager.COLUMN_TOTAL_SIZE_BYTES
import android.content.Context
import android.content.Context.DOWNLOAD_SERVICE
import android.database.Cursor
import android.net.Uri
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
import kotlinx.coroutines.*
import java.io.File

class LegacyFileDownloader {
    private val trafficStatsThreadId = 10001
    var errorMessage: String? = null
        private set
    var errorException: Throwable? = null
        private set
    var onProgress: (progressInPercent: Int?, totalMB: Long) -> Unit = @WorkerThread { _, _ -> }

    // fallback to register for news for existing download
    var currentDownloadResult: Deferred<Boolean>? = null

    @MainThread
    suspend fun downloadFileAsync(context: Context, url: String, file: File): Deferred<Boolean> {
        return withContext(Dispatchers.IO) {
            currentDownloadResult = async {
                try {
                    downloadFileInternal(context, url, file)
                } catch (e: Exception) {
                    errorMessage = e.localizedMessage
                    errorException = e
                    false
                }
            }
            currentDownloadResult!!
        }
    }

    @WorkerThread
    private suspend fun downloadFileInternal(context: Context, url: String, file: File): Boolean {
        val request = DownloadManager.Request(Uri.parse(url))
        val downloadService = context.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
        val downloadId = downloadService.enqueue(request)

        repeat(10 * 60 * 1000 / 200) {
            val query = DownloadManager.Query()
                .setFilterById(downloadId)
            downloadService.query(query).use { cursor ->
                if (cursor.moveToFirst()) {
                    val statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
                    require(statusIndex >= 0)
                    when (cursor.getInt(statusIndex)) {
                        DownloadManager.STATUS_FAILED -> return false
                        DownloadManager.STATUS_RUNNING -> {
                            val (progress, mb) = getDownloadProgress(cursor)
                            onProgress(progress, mb)
                        }
                        DownloadManager.STATUS_SUCCESSFUL -> {
                            copyDownloadToFile(context, downloadService, file, downloadId)
                            return true
                        }
                        else -> {}
                    }
                }
            }
            delay(200)
        }
        return false
    }

    private fun getDownloadProgress(cursor: Cursor): Pair<Int?, Long> {
        val totalSizeBytesIndex = cursor.getColumnIndex(COLUMN_TOTAL_SIZE_BYTES)
        require(totalSizeBytesIndex >= 0)
        val total = cursor.getLong(totalSizeBytesIndex)
        if (total < 0) {
            return Pair(0, 0)
        }

        val downloadedBytesIndex = cursor.getColumnIndex(COLUMN_BYTES_DOWNLOADED_SO_FAR)
        require(downloadedBytesIndex >= 0)
        val downloaded = cursor.getLong(downloadedBytesIndex)
        val progress = ((downloaded * 100L) / total).toInt()
        return Pair(progress, downloaded)
    }

    private fun copyDownloadToFile(
        context: Context,
        downloadService: DownloadManager,
        file: File,
        downloadId: Long
    ) {
        val uri = downloadService.getUriForDownloadedFile(downloadId)
        val stream = context.contentResolver.openInputStream(uri)
        requireNotNull(stream).buffered().use { downloadStream ->
            file.outputStream().buffered().use { fileStream ->
                downloadStream.copyTo(fileStream)
            }
        }
    }
}